# LeetCode 34、在排序数组中查找元素的第一个和最后一个位置

# 一、题目描述

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]

进阶:

  • 你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?

示例 1:

输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]

示例 2:

输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]

示例 3:

输入:nums = [], target = 0
输出:[-1,-1]

提示:

  • 0 <= nums.length <= 105
  • -10(9) <= nums[i] <= 10(9)
  • nums 是一个非递减数组
  • -10(9) <= target <= 10(9)

# 二、题目解析

# 三、参考代码

class Solution {
    public int[] searchRange(int[] nums, int target) {
        
        // 寻找目标值在数组中的开始位置
        int firstIdx = findBeginPostion(nums,target);

        // 寻找目标值在数组中的结束位置
        int lastIdx = findEndPostion(nums,target);

        // 返回寻找的结果
        return new int[]{firstIdx,lastIdx};

    }

    // 寻找目标值在数组中的开始位置
    private int findBeginPostion(int[] nums , int target){

        // left 指向当前区间的最左边位置,所以初始化为 0
        int left = 0;

        // right 指向当前区间的最右边位置,所以初始化为 nums.length - 1
        int right = nums.length - 1;

        // 循环进行二分查找,直到左端点位置超过了右端点
        // 或者在循环过程中找到了起始位置
        while(left <= right){

            // 计算当前区间的中间位置,向下取整
            int mid = (left + right) / 2;

            // 如果中间位置的元素值等于目标值 target
            if(nums[mid] == target){

                // 并且中间位置 mid 的左边没有元素,即中间位置 mid 为当前区间的起始位置
                // 或者中间位置 mid 的前一个元素小于目标值 target
                // 说明 mid 指向了 target 的起始位置
                if(mid == 0 || nums[mid - 1] < target){
                    // mid 指向了 target 的起始位置,返回这个结果
                    return mid;
                }

                // 否则,说明 mid 的左边依然有元素值等于 target
                // 那么 mid 就不是 target 的起始位置,需要在 mid 的左边进行查找
                // 所以缩小范围为 left 到 mid - 1
                // 当前区间的左侧为 left,右侧 right = mid - 1
                right = mid - 1;

            // 如果中间位置的元素值大于目标值 target
            // 说明需要在 mid 的左边进行查找
            }else if( nums[mid] > target){

                // 所以缩小范围为 left 到 mid - 1
                // 当前区间的左侧为 left,右侧 right = mid - 1
                right = mid - 1;

            // 如果中间位置的元素值小于目标值 target
            // 说明需要在 mid 的右边进行查找
            }else{

                // 所以缩小范围为 mid + 1 到 right
                // 当前区间的左侧为 left = mid + 1,右侧 right 
                left = mid + 1;

            }
        }
        
        // 如果循环结束后还没有返回,说明找不到目标值 target,返回 -1
        return - 1;
    }

    // 寻找目标值在数组中的结束位置
    private int findEndPostion(int[] nums , int target){

        // left 指向当前区间的最左边位置,所以初始化为 0
        int left = 0;

        // right 指向当前区间的最右边位置,所以初始化为 nums.length - 1
        int right = nums.length - 1;

        // 循环进行二分查找,直到左端点位置超过了右端点
        // 或者在循环过程中找到了结束位置    
        while(left <= right){

            // 计算当前区间的中间位置,向下取整
            int mid = (left + right) / 2;

            // 如果中间位置的元素值等于目标值 target
            if(nums[mid] == target){
                // 并且中间位置 mid 的右边没有元素,即中间位置 mid 为当前区间的结束位置
                // 或者中间位置 mid 的后一个元素大于目标值 target
                // 说明 mid 指向了 target 的结束位置
                if(mid == nums.length - 1 || nums[mid + 1] > target){
                    // mid 指向了 target 的结束位置,返回这个结果
                    return mid;
                }

                // 否则,说明 mid 的右边依然有元素值等于 target
                // 那么 mid 就不是 target 的结束位置,需要在 mid 的右边进行查找
                // 所以缩小范围为 mid + 1 到 right
                // 当前区间的左侧为 left = mid + 1 ,右侧为 right 
                left = mid + 1;

            // 如果中间位置的元素值大于目标值 target
            // 说明需要在 mid 的左边进行查找
            }else if( nums[mid] > target){

                // 所以缩小范围为 left 到 mid - 1
                // 当前区间的左侧为 left,右侧 right = mid - 1
                right = mid - 1;

            // 如果中间位置的元素值小于目标值 target
            // 说明需要在 mid 的右边进行查找
            }else{

                // 所以缩小范围为 mid + 1 到 right
                // 当前区间的左侧为 left = mid + 1,右侧 right 
                left = mid + 1;

            }
        }

        // 如果循环结束后还没有返回,说明找不到目标值 target,返回 -1
        return - 1;    
    }

}